# -*- mode: python -*-
###
#
# (c) 2000-2007 Wijnand Modderman <wijnand at tehmaze dot com>
# All rights reserved. See docs/LICENSE
#
###

__revision__ = '$Hg: .skell,v 979d136275fb Sun Jan 14 18:37:34 2007 +0100 $'

# system imports
import time
import re

# DDB imports
from ddb.lang import _
from ddb.log import log
from ddb.transport import ITransport, ITransportProtocol
from ddb.user import user
from ddb.settings import *
from ddb.ssl import SSLContext
from ddb.event import event

# Twisted imports
from twisted.internet import protocol, reactor
from twisted.protocols import basic
from twisted.words.xish import domish
from twisted.words.protocols.jabber import client, jid, xmlstream

# Check for SASL support
try:
    from twisted.words.protocols.jabber import sasl

    class jabberAuthSASL(client.XMPPAuthenticator):
        def __init__(self, jid, secret):
            log.msg(_('Jabber SASL authentication request for %s') % jid.full(), debug=True)
            client.XMPPAuthenticator.__init__(self, jid, secret)
except ImportError:
    import warnings
    warnings.warn(_('Warning, you probably are using an old Twisted version, so SASL authentication is disabled'))
    sasl = None

    class jabberAuthSASL:
        def __init__(self, jid, secret):
            log.msg(_('Jabber SASL authentication not supported!'))
            return None

class jabberAuthPlain(client.BasicAuthenticator):
    def __init__(self, jid, secret):
        log.msg(_('Jabber PLAIN authentication request for %s') % jid.full(), debug=True)
        client.BasicAuthenticator.__init__(self, jid, secret)

class jabberProtocol(ITransportProtocol):
    """Jabber protocol skell."""
    type = 'Jabber'
    def __init__(self, factory):
        ITransportProtocol.__init__(self)
        self.factory = factory
        self.transport = factory
        self.xs      = None

    #
    # protocol stuff
    #

    def connectionMade(self):
        ITransportProtocol.connectionMade(self)
        ITransportProtocol.openedConference(self, 'roster')
        if self.factory.config.has_key('conference'):
            for conference in self.factory.config['conference']:
                print 'will join', conference
                if ' ' in conference:
                    conference, key = conference.split(' ', 1)
                else:
                    key = None
                self.joinConference(conference, key)

    def joinConference(self, conference, key=None):
        node = domish.Element(('jabber:client','presence'))
        node['from'] = self.factory.jid.full()
        node['to']   = conference
        self.xs.send(node)
        ITransportProtocol.openedConference(self, conference)
        

    def send(self, node):
        print '>>>', node.toXml()
        self.xs.send(node)

    def authd(self, xs):
        """We are authorized by the server."""
        self.xs = xs 
        node = domish.Element(('jabber:client','presence'))
        xs.send(node)
        self.connectionMade()
        xs.addObserver('/message',  self.message)
        xs.addObserver('/presence', self.presence)
        xs.addObserver('/iq',       self.iq)
        # XXX Enable for debugging
        # xs.addObserver('/*',        self.something)
        # XXX 
    
    def message(self, el):
        # 2006/12/31 16:07 +0200 [XmlStream,client] message <message xmlns='jabber:client' to='ddb@jabber.xs4all.nl/DDB' from='ddb@conference.jabber.xs4all.nl/tehmaze' type='groupchat'><body>!ps</body><x xmlns='jabber:x:delay' stamp='20061231T14:05:25'/></message>
        if hasattr(el, 'x') and el.x:
            # dirty!
            if "xmlns='jabber:x:delay'" in el.x.toXml():
                # backlogged message, ignore
                return
        print 'message', el.toXml()
        if el.attributes.has_key('type'):
            rawmessage = str(el.body)
            if el.attributes['type'] == 'chat':
                # one-on-one chat
                # <message xmlns='jabber:client' to='ddbbot@gmail.com' type='chat' id='911963AFD1EBD15C1' from='michiel@freecode.nl/gmail.996DD0A9'><body>!test</body><met:google-mail-signature xmlns:met='google:metadata'>4a24450506d1256a</met:google-mail-signature><cha:active xmlns:cha='http://jabber.org/protocol/chatstates'/><nos:x xmlns:nos='google:nosave' value='disabled'/><arc:record xmlns:arc='http://jabber.org/protocol/archive' otr='false'/></message>
                target         = el.attributes['from'].split('@')[0]+'!'+el.attributes['from'].split('/')[0]
                senderIdentity = el.attributes['from']
            elif el.attributes['type'] == 'groupchat':
                # conference actions
                # we can get topic updates;
                # 2006/12/31 16:40 +0200 [XmlStream,client] message <message xmlns='jabber:client' to='ddb@jabber.xs4all.nl/DDB' from='ddb@conference.jabber.xs4all.nl' type='groupchat'><subject>http://code.google.com/p/ddbbot</subject><body>tehmaze has set the subject to: http://code.google.com/p/ddbbot</body></message>
                if el.subject:
                    if el.body:
                        setby = str(el.body).split()[0]
                    else:
                        setby = el.attributes['from']
                    self.conferences.set(el.attributes['from'], topic={
                        'setby': setby,
                        'setat': time.time(),
                        'topic': el.subject
                        })
                    return
                # and just conference messages
                target         = el.attributes['from'].split('/')[0]
                senderIdentity = el.attributes['from'].split('/')[1]
            else:
                log.msg(_('Don\'t know how to handle chat type "%s"') % el.attributes['type'])
                return
            # build an event with the gathered data
            log.msg(_('Message from %s on %s: %s') % (senderIdentity, target, rawmessage), debug=True)
            user = self.users.update(senderIdentity, target)
            event(self, 'message', user=user, target=target, message=rawmessage, chattype=el.attributes['type'])

    def presence(self, el):
        senderIdentity = el.attributes['from'].split('/')[0]
        senderMask     = el.attributes['from'].split('@')[0]+'!'+senderIdentity
        if el.attributes.has_key('type'):
            t = el.attributes['type']
            if t == 'subscribe':
                # Grant every subscription request
                node = domish.Element(('jabber:client', 'presence'))
                node['to'] = el.attributes['from']
                node['type'] = 'subscribed'
                self.xs.send(node)
                node['type'] = 'subscribe'
                self.xs.send(node)
            elif t == 'unavailable':
                ITransportProtocol.partedConference(self, senderIdentity, senderMask, 'roster')
        else:
            print 'presence', el.toXml()
            # did they rename my JID? (google for example does this)
            # <presence xmlns='jabber:client' to='ddbbot@gmail.com/DDB93928BBB' from='ddbbot@gmail.com/gmail.9971307C'><priority>24</priority><caps:c xmlns:caps='http://jabber.org/protocol/caps' node='http://mail.google.com/xmpp/client/caps' ver='1.1'/><x xmlns='vcard-temp:x:update'/></presence>
            if "xmlns='vcard-temp:x:update'" in el.toXml():
                myOldJID = self.factory.jid.full()
                myNewJID = el.attributes['to']
                if myNewJID.startswith(myOldJID) and myNewJID != myOldJID:
                    log.msg(_('Server updated my JID from %s -> %s') % (myOldJID, myNewJID), debug=True)
                    self.factory.jid = jid.JID(myNewJID)
                return
            # sign on request?
            # <presence xmlns='jabber:client' to='ddb@jabber.xs4all.nl/DDB' from='ddbbot@conference.jabber.xs4all.nl/dunker'><x xmlns='http://jabber.org/protocol/muc#user'><item affiliation='none' jid='dunker@jabber.xs4all.nl/Gaim' role='participant'/></x></presence>
            senderJID = None
            if el.x and el.x.item and el.x.item.attributes.has_key('jid'):
                 senderJID = el.x.item.attributes['jid']
            if not senderJID:
                # anonymous signon
                ITransportProtocol.joinedConference(self, senderIdentity, senderMask, el.attributes['from'])
            else:
                senderIdentity = el.attributes['from'].split('/')[1]
                senderMask     = senderIdentity+'!'+senderJID.split('/')[0]
                ITransportProtocol.joinedConference(self, senderIdentity, senderMask, el.attributes['to'])

    def iq(self, el):
        print 'got iq', el.toXml()

    def something(self, el):
        print 'got something', el.attributes, el.toXml()
        print dir(el.toXml)

    #
    # calls
    #

    def _tohtml(self, msg):
        for x in re.findall('(\002([^\002]+)\002)', msg):
            msg = msg.replace(x[0], x[1])
        return msg

    def reply(self, event, msg):
        node = domish.Element(('jabber:client', 'message'))
        node['from'] = self.factory.jid.full()
        if hasattr(event.rawevent, 'chattype'):
            chattype = event.rawevent.chattype
        else:
            chattype = event.chattype
        node['type'] = chattype
        if node['type'] in ['groupchat']:
            node['to'] = event.rawevent.target
            msg = '%s: %s' % (event.user.identity.split('@')[0], msg)
        elif node['type'] in ['chat']:
            node['to'] = event.user.identity
        node.addElement('body', content=self._tohtml(msg))
        self.xs.send(node)
        print '>>>', node.toXml()

class jabberFactory(ITransport, xmlstream.XmlStreamFactory):
    protocol = jabberProtocol
    def __init__(self, manager, name, config):
        self.manager = manager
        self.name    = name
        self.config  = config
        self.xs      = None
        self.handler = self.protocol(self)
        if not hasattr(self, 'authenticator'):
            self.authenticator = jabberAuthPlain(self.jid, self.secret)
        ITransport.__init__(self, manager, name, config)
        xmlstream.XmlStreamFactory.__init__(self, self.authenticator)

class transport(ITransport):
    """Jabber Transport base class."""
    protocol = jabberProtocol
    factory  = jabberFactory
    #event    = ircEvent
    
    def __init__(self, manager, name, config):
        ITransport.__init__(self, manager, name, config)
        log.msg(_('Starting %s') % str(self.__class__))
        try:
            serverHost = config['host'][0]
            try:
                serverPort = int(config['port'][0])
            except KeyError:
                serverPort = 5222
            try:
                serverSSL = int(config['ssl'][0])
            except KeyError:
                serverSSL = False
            try:
                serverTLS = int(config['tls'][0])
            except KeyError:
                serverTLS = False
        except KeyError, e:
            log.msg(_('Could not start transport, missing configuration option "%s"') % str(e))
            return
        self.factory.jid = jid.JID(config['identity'][0])
        self.factory.secret = config['password'][0]
        self.server = self.factory(manager, name, config)
        self.server.addBootstrap('//event/stream/authd', self.server.handler.authd)
        if serverTLS:
            self.server.authenticator = jabberAuthSASL(self.factory.jid, self.factory.secret)
            reactor.connectTCP(serverHost, serverPort, self.server)
        elif serverSSL:
            reactor.connectSSL(serverHost, serverPort, self.server, SSLContext())
        else:
            reactor.connectTCP(serverHost, serverPort, self.server)

    def start(self):
        self.run = True

    def stop(self):
        self.run = False

# vim:ts=4:sw=4:et:
